# @Time : 2024-11-14 10:23
# @Author : Fioman 
# @Phone : 13149920693
"""
eap通信过程中用到的一些数据模型,比如用到的一些常量啊,枚举类型啊,数据类类型等.
"""
import threading
import time
from enum import Enum
from typing import Dict

import gevent

from eap_control import eap_tool

TimeOut = "TimeOut"

AlarmDict = {
    "1": ["A", "上料控制器急停"],
    "2": ["A", "裁切机控制器急停"],
    "3": ["A", "大铡刀控制器急停"],
    "4": ["A", "小铡刀控制器急停"],
    "5": ["A", "相机组通信异常"],
    "6": ["A", "上料控制器通信异常"],
    "7": ["A", "裁切机控制器通信异常"],
    "8": ["A", "大铡刀控制器通信异常"],
    "9": ["A", "小铡刀控制器通信异常"],
    "10": ["A", "物料检测异常"],
    "11": ["A", "线扫描识别异常"],
    "12": ["A", "大铡刀视觉检测异常"],
    "13": ["A", "小铡刀视觉检测异常"],
    "14": ["A", "上料轴报警异常"],
    "15": ["A", "裁切机轴报警异常"],
    "16": ["A", "大铡刀轴报警异常"],
    "17": ["A", "小铡刀轴报警异常"],
}


class DownloadType(Enum):
    A = 0  # 新增
    D = 1  # 删除(Delete)
    U = 2  # 修正


class LotPortType(Enum):
    Start = 0  # 开始
    Cancel = 1  # 取消
    About = 2  # 中断


class ClockType(Enum):
    """
    用户登录登出类型
    """
    I = 0  # ClockIn
    O = 1  # ClockOut
    Q = 2  # Query


class LotEndSignal(Enum):
    """
    结批信号
    0: 结批信号off
    1:  结批信号on
    """
    on = 1
    off = 0


class IsStopReceive(Enum):
    """
    Y: 停止收片 并Warning
    N: 正常方片
    """
    Y = 0
    N = 1


class ProdeMixMode(Enum):
    """
    ON: 混批管控开放
    OFF:混批管控关闭
    N/A: 无管控
    """
    ON = 1
    OFF = 2
    N_A = 3


class StreamConnStatus(Enum):
    """
    上游和下游设备的在线和离线状态
    """
    OFFLINE = 0  # 离线
    INLINE = 1  # 在线


class EmergencyMode(Enum):
    """
    设备紧急模式
    Normal: Normal Mode
    Emergency: Emergency Stop Mode
    """
    Normal = 0
    Emergency = 1


class JobCountType(Enum):
    In = 0  # 板子流入
    Out = 1  # 板子流出


class DeviceStatus(Enum):
    """
    设备状态,对应发送给eap的消息为status_id
    """
    Idle = 0  # 闲置
    Run = 1  # 运行
    Stop = 2  # 停止
    Initial = 3  # 初始化
    Down = 4  # Down机
    PM = 5  # 维护


class AlarmType(Enum):
    """
    报警类型
    S:发生报警
    R:解除报警
    """
    S = "发生报警"
    R = "解除报警"


class EapConstant:
    """
    Eap开发过程中用到的一些常量,用变量表示,不容易写错
    """
    TimeOut = "TimeOut"


class DataResult:
    """
    函数操作通用返回值,有操作是否成功,操作的返回数据,还有操作成功或者失败的提示消息
    """

    def __init__(self):
        self.state = False  # 默认是操作失败
        self.data = None
        self.info = ""


class RealStatusData:
    _instance = None
    _initFlag = False
    _lock = threading.Lock()
    _lastChange = time.time()  # 最近一次状态变化的时间 .

    def __new__(cls, *args, **kwargs):
        with cls._lock:
            if cls._instance is None:
                cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, cimMode, autoMode, userId, idleDelay, prodMode, programVersion):
        if not RealStatusData._initFlag:
            self._cimMode = cimMode
            self._autoMode = autoMode
            self._userId = userId
            self._idleDelay = idleDelay
            self._prodMode = prodMode
            self._programVersion = programVersion
            self._emergencyMode = EmergencyMode.Normal
            self._statusId = DeviceStatus.Idle.name
            self._alarmId = ""  # 默认没有报警,报警代码是空的
            self._upConnStatus = StreamConnStatus.INLINE
            self._downConnStatus = StreamConnStatus.INLINE
            self._lotId = ""  # 当前的批号是空的,还没有生产,当批号发生变化的时候再改变
            self._prodMixMode = ProdeMixMode.ON  # 混批管控
            self._isStopRecvive = IsStopReceive.N  # 正常放片
            self._lotEndSignal = LotEndSignal.off  # 结批信号是否打开
            self._queueLotCount = 0  # 目前设备上还持有多少个工单等待投入生产
            RealStatusData._initFlag = True
            self.statusReportTimer = None  # 向Eap发送报告状态的定时器
            self.statusChangeReportTimer = None  # 状态发生改变的定时器

    @property
    def cimMode(self):
        return self._cimMode

    @cimMode.setter
    def cimMode(self, value):
        if value != self._cimMode:
            self._cimMode = value
            self.real_status_report()
            gevent.sleep(0.1)
            self.status_change_report()

    @property
    def autoMode(self):
        return self._autoMode

    @autoMode.setter
    def autoMode(self, value):
        if value != self._autoMode:
            self._autoMode = value
            self.real_status_report()
            gevent.sleep(0.1)
            self.status_change_report()

    @property
    def userId(self):
        return self._userId

    @userId.setter
    def userId(self, value):
        if value != self._userId:
            self._userId = value
            self.real_status_report()

    @property
    def idleDelay(self):
        return self._idleDelay

    @idleDelay.setter
    def idleDelay(self, value):
        if value != self._idleDelay:
            self._idleDelay = value
            self.real_status_report()

    @property
    def prodMode(self):
        return self._prodMode

    @prodMode.setter
    def prodMode(self, value):
        if value != self._prodMode:
            self._prodMode = value
            self.real_status_report()

    @property
    def programVersion(self):
        return self._programVersion

    @programVersion.setter
    def programVersion(self, value):
        if value != self._programVersion:
            self._programVersion = value
            self.real_status_report()

    @property
    def emergencyMode(self):
        return self._emergencyMode

    @emergencyMode.setter
    def emergencyMode(self, value):
        if value != self._emergencyMode:
            self._emergencyMode = value
            self.real_status_report()

    @property
    def statusId(self):
        return self._statusId

    @statusId.setter
    def statusId(self, value):
        if value != self._statusId:
            self._statusId = value
            self.real_status_report()
            gevent.sleep(0.1)
            self.status_change_report()

    @property
    def alarmId(self):
        return self._alarmId

    @alarmId.setter
    def alarmId(self, value):
        if value != self._alarmId:
            self._alarmId = value
            self.real_status_report()
            gevent.sleep(0.1)
            self.status_change_report()

    @property
    def upConnStatus(self):
        return self._upConnStatus

    @upConnStatus.setter
    def upConnStatus(self, value):
        if value != self._upConnStatus:
            self._upConnStatus = value
            self.real_status_report()

    @property
    def downConnStatus(self):
        return self._downConnStatus

    @downConnStatus.setter
    def downConnStatus(self, value):
        if value != self._downConnStatus:
            self._downConnStatus = value
            self.real_status_report()

    @property
    def lotId(self):
        return self._lotId

    @lotId.setter
    def lotId(self, value):
        if value != self._lotId:
            self._lotId = value
            self.real_status_report()

    @property
    def prodMixMode(self):
        return self._prodMixMode

    @prodMixMode.setter
    def prodMixMode(self, value):
        if value != self._prodMixMode:
            self._prodMixMode = value
            self.real_status_report()

    @property
    def isStopRecvive(self):
        return self._isStopRecvive

    @isStopRecvive.setter
    def isStopRecvive(self, value):
        if value != self._isStopRecvive:
            self._isStopRecvive = value
            self.real_status_report()

    @property
    def lotEndSignal(self):
        return self._lotEndSignal

    @lotEndSignal.setter
    def lotEndSignal(self, value):
        if value != self._lotEndSignal:
            self._lotEndSignal = value
            self.real_status_report()

    @property
    def queueLotCount(self):
        return self._queueLotCount

    @queueLotCount.setter
    def queueLotCount(self, value):
        if value != self._queueLotCount:
            self._queueLotCount = value
            self.real_status_report()

    def real_status_report(self):
        """
        如果两次的调用间隔小于0.5秒,就认为是同时调用的.做一个定时器,状态发生改变的时候0.5秒之后再调用
        如果时间在0.5秒以内的就取消掉之前的调用,然后开启一个新的定时器,然后就是会发送最新的状态
        @return:
        """
        if self.statusReportTimer is not None:
            self.statusReportTimer.cancel()
        self.statusReportTimer = threading.Timer(0.5, self.execute_report)
        self.statusReportTimer.start()

    def execute_report(self):
        from eap_control.eap_request import request_real_status_report
        from eap_control.response_parse import parse_real_status_change_response
        response = request_real_status_report(self.get_body_data())
        parse_real_status_change_response(response)

    def status_change_report(self):
        """
        如果两次的调用间隔小于0.5秒,就认为是同时调用的.做一个定时器,状态发生改变的时候0.5秒之后再调用
        如果时间在0.5秒以内的就取消掉之前的调用,然后开启一个新的定时器,然后就是会发送最新的状态
        @return:
        """
        if self.statusChangeReportTimer is not None:
            self.statusChangeReportTimer.cancel()
        self.statusChangeReportTimer = threading.Timer(0.5,
                                                       self.excute_status_change_report)
        self.statusChangeReportTimer.start()

    def excute_status_change_report(self):
        from eap_control.eap_request import request_status_change_report
        from eap_control.response_parse import parse_status_change_response
        response = request_status_change_report(self.get_status_change_body())
        parse_status_change_response(response)

    def get_body_data(self):
        """
        获取body数据.
        @return:
        """
        body = {
            "cim_mode": self.cimMode,
            "auto_mode": self.autoMode,
            "emergency_mode": self.emergencyMode.name,
            "status_id": self.statusId,
            "alarm_id": self.alarmId,
            "user_id": self.userId,
            "idle_delay_time": self.idleDelay,
            "upstream_connect_status": self.upConnStatus.name,
            "downstream_connect_status": self.downConnStatus.name,
            "lot_id": self.lotId,
            "prod_mode": self.prodMode,
            "prod_mix_mode": self.prodMixMode.name,
            "is_stop_receive": self.isStopRecvive.name,
            "program_version": self.programVersion,
            "lot_end_signal": self.lotEndSignal.value,
            "queue_lot_count": self.queueLotCount,
        }
        return body

    def get_alarm_report_body(self, alarmType, alarmId:int)-> Dict:
        """
        获取发送给Eap的bodyDict
        @return:
        """
        alarmCode, alarmDesc = AlarmDict.get(str(alarmId))
        requestBody = {
            "report_dt": eap_tool.get_now(),
            "alarm_type": alarmType.name,
            "alarm_code": alarmCode,
            "alarm_id": int(alarmId),
            "alarm_desc": alarmDesc,
            "status_id": self.statusId
        }
        return requestBody

    def get_status_change_body(self):
        """
        获取发送给Eap的bodyDict
        request_body = {
        "report_dt": get_now(),
        "cim_mode": ec.cim_mode,
        "auto_mode": ec.auto_mode,
        "status_id": ec.status_id,
        "alarm_id": ec.alarm_id,
            }
        @return:
        """
        requestBody = {
            "report_dt": eap_tool.get_now(),
            "cim_mode": self.cimMode,
            "auto_mode": self.autoMode,
            "status_id": self.statusId,
            "alarm_id": self.alarmId,
        }
        return requestBody


class LotOrder:
    """
    lotOrder数据模型
    """

    def __init__(self):
        self.lotId = None  # 批号
        self.prodId = None  # 料号
        self.width = None  # 板宽
        self.height = None  # 板高
        self.thickness = "0.33"  # 铜箔厚度
        self.jointNumber = "4"  # 几拼板
        self.lotQty = None  # 计划生产数量
        self.finished = 0  # 已完成数量
